import datetime
import json

from flask import Markup, g, request
from flask_appbuilder import Model
from sqlalchemy import (
    Column,
    DateTime,
    Enum,
    ForeignKey,
    Integer,
    String,
    Text,
)
from sqlalchemy.orm import relationship

from myapp.app import app
from myapp.const.service import EnumNotebookStatus
from myapp.models.base import MyappModelBase
from myapp.models.helpers import AuditMixinNullable
from myapp.utils.strings import convert_to_pinyin


metadata = Model.metadata
conf = app.config


class Notebook(Model, AuditMixinNullable, MyappModelBase):
    __tablename__ = 'notebook'
    id = Column(Integer, primary_key=True)
    project_id = Column(Integer, ForeignKey('project.id'), nullable=False)  # 定义外键
    project = relationship('Project', foreign_keys=[project_id])
    name = Column(String(200), unique=True, nullable=True)
    describe = Column(String(200), nullable=True)
    namespace = Column(String(200), nullable=True, default='jupyter')
    images = Column(String(200), nullable=True, default='')
    image_id = Column(Integer, nullable=True, comment='镜像ID')
    ide_type = Column(String(100), default='jupyter')
    working_dir = Column(String(200), default='')  # 挂载
    volume_mount = Column(
        String(400), default='kubeflow-user-workspace(pvc):/mnt,kubeflow-archives(pvc):/archives'
    )  # 挂载
    node_selector = Column(String(200), default='cpu=true,notebook=true')  # 挂载
    image_pull_policy = Column(Enum('Always', 'IfNotPresent'), nullable=True, default='Always')
    resource_memory = Column(String(100), default='10G')
    resource_cpu = Column(String(100), default='10')
    resource_gpu = Column(String(100), default='0')
    expand = Column(Text(65536), default='')
    ssh_port = Column(Integer, nullable=True, unique=True, comment='ssh端口')
    ssh_secret = Column(Text(65536), nullable=True, comment='ssh密钥')
    status = Column(String(100), comment='状态')
    specifications = Column(String(100), default='', comment='资源规格')
    pool_id = Column(String(100), default='', comment='资源池ID')
    auto_stop = Column(String(1000), comment='自动停止')
    err_msg = Column(String(1000), comment='错误信息')
    running_start_time = Column(DateTime, nullable=True, comment='运行开始时间')
    acc_running_time = Column(Integer, comment='运行时长')
    region = Column(
        String(100), nullable=False, default='default', server_default='default', comment='地区'
    )

    def __repr__(self):
        rand_str = ''
        if self.ssh_secret:
            rand_str = self.ssh_secret[:4]

        name = convert_to_pinyin(self.name) + '-' + str(self.id) + '-' + rand_str
        if name[0].isdigit():
            name = 'n' + name
        return name.lower()

    @property
    def route_prefix(self):
        return '/notebook/' + self.namespace + '/' + repr(self) + '/'

    @property
    def url_path(self):
        if self.ide_type == 'theia':
            return self.route_prefix + '#' + self.mount
        return self.route_prefix + 'lab?#' + self.mount

    @property
    def name_url(self):
        if self.ide_type == 'theia':
            url = '/notebook/' + self.namespace + '/' + repr(self) + '/' + '#' + self.mount
        else:
            url = '/notebook/' + self.namespace + '/' + repr(self) + '/lab?#' + self.mount

        # url= url + "#"+self.mount
        JUPYTER_DOMAIN = self.project.cluster.get('JUPYTER_DOMAIN', request.host)
        if JUPYTER_DOMAIN:
            host = 'http://' + JUPYTER_DOMAIN
        else:
            host = request.host_url.strip('/')  # 使用当前域名打开

        # 对于有边缘节点，直接使用边缘集群的代理ip
        SERVICE_EXTERNAL_IP = (
            json.loads(self.project.expand).get('SERVICE_EXTERNAL_IP', None)
            if self.project.expand
            else None
        )
        if SERVICE_EXTERNAL_IP:
            service_ports = 10000 + 10 * self.id
            host = 'http://%s:%s' % (SERVICE_EXTERNAL_IP, str(service_ports))
            if self.ide_type == 'theia':
                url = '/' + '#/mnt/' + self.created_by.username
            else:
                url = '/notebook/jupyter/%s/lab/tree/mnt/%s' % (
                    self.name,
                    self.created_by.username,
                )
        return Markup(f'<a target=_blank href="{host}{url}">{self.name}</a>')

    @property
    def mount(self):
        # if "(hostpath)" in self.volume_mount:
        #     mount = self.volume_mount[self.volume_mount.index('(hostpath)'):]
        #     mount=mount.replace('(hostpath):','')
        #     if ',' in mount:
        #         mount = mount[:mount.index(',')]
        #         return mount
        #     else:
        #         return mount
        # else:
        if self.created_by:
            return f'/mnt/publish-data/{self.created_by.username}/notebook/{repr(self)}'
        else:
            return f'/mnt/publish-data/{g.user.username}/notebook'

    @property
    def resource(self):
        return (
            self.resource_cpu
            + '(cpu)'
            + self.resource_memory
            + '(memory)'
            + self.resource_gpu
            + '(gpu)'
        )

    # @property
    # def status(self):
    #     try:
    #         k8s_client = py_k8s.K8s(self.cluster.get('KUBECONFIG', ''))
    #         namespace = conf.get('NOTEBOOK_NAMESPACE')
    #         pods = k8s_client.get_pods(namespace=namespace, pod_name=self.name)
    #         status = pods[0]['status']
    #         if g.user.is_admin():
    #             k8s_dash_url = self.cluster.get('K8S_DASHBOARD_CLUSTER') +
    # "#/search?namespace=jupyter&q=" + self.name
    #             url = Markup(f'<a target=_blank href="{k8s_dash_url}">{status}</a>')
    #             return url
    #         return status
    #
    #     except Exception as e:
    #         print(e)
    #         return "unknown"

    @property
    def cluster(self):
        if self.project:
            return self.project.cluster
        else:
            return conf.get('CLUSTERS')[conf.get('ENVIRONMENT')]

    # 清空激活
    @property
    def renew(self):
        object_info = conf.get('CRD_INFO', {}).get('notebook', {})
        timeout = int(object_info.get('timeout', 60 * 60 * 24 * 3))

        end = self.changed_on + datetime.timedelta(seconds=timeout)
        end = end.strftime('%Y-%m-%d')
        return Markup(f'<a href="/notebook_modelview/renew/{self.id}">截至 {end}</a>')

    def get_node_selector(self):
        return self.get_default_node_selector(
            self.project.node_selector, self.resource_gpu, 'notebook'
        )

    # 清空激活
    @property
    def reset(self):
        return Markup(f'<a href="/notebook_modelview/reset/{self.id}">reset</a>')

    def get_auto_stop(self):
        default_val = {'enable': False}
        val = {'enable': False}
        try:
            val = json.loads(self.auto_stop)
        except Exception:
            pass

        if val is None:
            return default_val

        if 'enable' not in val:
            val['enable'] = False

        if val['enable']:
            if 'seconds' not in val:
                val['seconds'] = 6 * 3600
            else:
                val['seconds'] = int(val['seconds'])

        return val

    def get_acc_running_time(self):
        if self.status != EnumNotebookStatus.running.value:
            return self.acc_running_time

        if self.running_start_time is None:
            return self.acc_running_time

        acc_running_time = self.acc_running_time
        time_diff = datetime.datetime.now() - self.running_start_time
        if acc_running_time is None:
            acc_running_time = 0
        if time_diff.total_seconds() > 0:
            return acc_running_time + time_diff.total_seconds()

        return acc_running_time

    def get_remain_running_time(self):
        if self.status != EnumNotebookStatus.running.value:
            return None

        auto_stop = self.get_auto_stop()
        if auto_stop['enable'] is False:
            return None

        if self.running_start_time is None:
            return auto_stop['seconds']

        time_diff = datetime.datetime.now() - self.running_start_time
        if time_diff.total_seconds() < 0:
            return auto_stop['seconds']

        ret = auto_stop['seconds'] - time_diff.total_seconds()
        if ret < 0:
            return 0

        return ret
